-include_lib("templ_delim.hrl").

-define(InitPos(Pos), [Pos]).
-define(IncPos(Pos), [lists:nth(1,Pos)+1|lists:nthtail(1,Pos)]).
-define(GetPos(Pos), lists:nth(1,Pos)).
-define(ResetPos(Pos, Val), [Val|lists:nthtail(1,Pos)]).
-define(PushPos(Pos, Val), [Val|Pos]).
-define(PopPos(Pos), lists:nthtail(1,Pos)).

%% @type stream() = string()
%% @type stack() = [any()]
%% @type tok() = {atom(), [integer()]} | {atom(), [integer()], any()}
%% @type pos() = [integer()]
%% @type state() = [any()]
%% @type errors() = [any()]
%% @type contFun() = fun(stream(), stack(), [tok()], pos(), state(), errors(), string())
%% @type cont() = {stream(), stack(), [tok()], pos(), state(), errors(), string(), contFun()}

%% @type scan_result() = {ok, [tok()], pos()} 
%%         | {error, {pos(), templ_scan, atom()}, pos()}
%%         | {error, {pos(), templel_scan, atom()}, pos()}


%% @spec string(string()) -> scan_results()
%% @doc takes a list of characters and tries to tokenise them
%%
string(Cs) ->
	case string(Cs, 1) of
		{ok,Tokens,_}   -> 
			Tokens;
		{error,Where,_} ->
			throw({{?MODULE,einval},Where})
%	catch
%		error:What ->
%			throw({{?MODULE,einval},What})
	end.


%% TODO
%% @doc return a string describing the error
%%
format_error({string,Quote,Head}) ->
    ["unterminated " ++ string_thing(Quote) ++
     " starting with " ++ io_lib:write_string(Head,Quote)];
format_error({illegal,Type}) -> io_lib:fwrite("illegal ~w", [Type]);
format_error(char) -> "unterminated character";
format_error(scan) -> "premature end";
format_error({base,Base}) -> io_lib:fwrite("illegal base '~w'", [Base]);
format_error(float) -> "bad float";
%%
format_error(Other) -> io_lib:write(Other).

string_thing($') -> "atom";   %' Stupid Emacs
string_thing(_) -> "string".


%% @spec tokens(none|cont(), stream(), integer()) ->
%%	       {done, {ok, [tok()], EndPos}, Rest} |
%%	       {done, {error, {ErrorPos,?MODULE,What}, EndPos}, Rest} |
%%	       {more, cont()}
%% @doc this is the main function into the re-entrant scanner
%%
tokens(none, Chars, Pos) ->
    tokens({[],[],[],Pos,io,[],?RDelim,fun scan/7}, Chars, Pos);
tokens({Cs,_CStack,_Toks,Pos,eof,_Delim,_DStack,_Fun}, eof, _) ->
    {done,{eof,Pos},Cs};
tokens({Cs,CStack,Toks,Pos,_State,Errors,Delim,Fun}, eof, _) ->
    Fun(Cs++eof, CStack, Toks, Pos, eof, Errors, Delim);
tokens({Cs,CStack,Toks,Pos,State,Errors,Delim,Fun}, Chars, _) ->
    Fun(Cs++Chars, CStack, Toks, Pos, State, Errors, Delim).


%% scan utils

%%% String
%more(Cs, CStack, Toks, Pos, eos, Errors, Delim, DStack, Fun) ->
%    throw({{?MODULE,einval},[Cs,CStack,Toks,Pos,eos,Errors,Delim, DStack,Fun]});
%more(Cs, CStack, Toks, Pos, [], Errors, Delim, DStack, Fun) ->
%    Fun(Cs++eof, CStack, Toks, Pos, eos, Errors, Delim, DStack);
%%% Stream
%more(Cs, CStack, Toks, Pos, eof, Errors, Delim, DStack, Fun) ->
%    throw({{?MODULE,einval},[Cs,CStack,Toks,Pos,eof,Errors,Delim, DStack,Fun]});
%more(Cs, CStack, Toks, Pos, io, Errors, Delim, DStack, Fun) ->
%    {more,{Cs,CStack,Toks,Pos,io,Errors,Delim, DStack,Fun}}.

%% @doc wait for the stream to be available
%% string
more(Cs, CStack, Toks, Pos, eos, Errors, Delim, Fun) ->
    throw({{?MODULE,einval},[Cs,CStack,Toks,Pos,eos,Errors,Delim,Fun]});
more(Cs, CStack, Toks, Pos, [], Errors, Delim, Fun) ->
    Fun(Cs++eof, CStack, Toks, Pos, eos, Errors, Delim);
%% stream
more(Cs, CStack, Toks, Pos, eof, Errors, Delim, Fun) ->
    throw({{?MODULE,einval},[Cs,CStack,Toks,Pos,eof,Errors,Delim,Fun]});
more(Cs, CStack, Toks, Pos, io, Errors, Delim, Fun) ->
    {more,{Cs,CStack,Toks,Pos,io,Errors,Delim,Fun}}.
